package com.diploma.project.aiqiyi.utils;

import com.jcraft.jsch.Channel;
import com.jcraft.jsch.ChannelSftp;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.Session;
import com.jcraft.jsch.SftpException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.exception.ExceptionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Properties;

@Slf4j
public class SftpUtil {
    private static final Logger logger = LoggerFactory.getLogger(SftpUtil.class);
    private String host;
    private String username;
    private String password;
    private int port;
    private String priKeyFile;
    private String passphrase; // 私钥口令
    private ChannelSftp sftp;
    private Session session;

    public SftpUtil(String host, String username, String password, int port) {
        this.host = host;
        this.username = username;
        this.password = password;
        this.port = port;
    }

    public SftpUtil(String host, String username, String password, int port, String priKeyFile, String passphrase) {
        this.host = host;
        this.username = username;
        this.password = password;
        this.port = port;
        this.priKeyFile = priKeyFile;
        this.passphrase = passphrase;
    }

    /**
     * connect server via sftp
     */
    private ChannelSftp connect() {
        try {

            if (sftp != null && sftp.isConnected()) {
                return sftp;
            }

            JSch jsch = new JSch();
            if (!XString.isNullOrEmpty(priKeyFile)) {
                if (!XString.isNullOrEmpty(passphrase))
                    jsch.addIdentity(priKeyFile, passphrase);
                else
                    jsch.addIdentity(priKeyFile);
            }

            if (session == null || !session.isConnected()) {
                jsch.getSession(username, host, port);
                session = jsch.getSession(username, host, port);
                session.setPassword(password);
                session.setConfig("userauth.gssapi-with-mic", "no");
                Properties sshConfig = new Properties();
                sshConfig.put("StrictHostKeyChecking", "no");
                session.setTimeout(30000);
                session.setConfig(sshConfig);
                session.connect();
                logger.info("Session connected.");
            }

            Channel channel = session.openChannel("sftp");
            channel.connect();
            sftp = (ChannelSftp) channel;
            logger.info("Connected to {}", host);
        } catch (Exception e) {
            logger.error("Failed to get the channel {}", e);
            if (session != null) {
                session.disconnect();
                session = null;
            }
            sftp = null;
        }
        return sftp == null ? null : sftp;
    }

    /**
     * Disconnect with server
     */
    private void disconnect() {
        try {
            if (sftp != null) {
                sftp.disconnect();
                sftp = null;
            }
            if (session != null) {
                session.disconnect();
                session = null;
            }
            logger.info("sftp disconnect successfully");
        } catch (Exception e) {
            logger.error("sftp disconnect error {}", e);
        }
    }

    public boolean download(String directory, String downloadFile, String saveFile) {
        boolean success = false;
        try {
            ChannelSftp sftp = connect();
            if (sftp == null) {
                return false;
            }
            logger.info("Start to download the file {}, {} on remote server", directory, downloadFile);
            sftp.cd(directory);
            File file = new File(saveFile);
            sftp.get(downloadFile, new FileOutputStream(file));
            logger.info("成功下载文件：{} 到本地", saveFile);
            success = true;
        } catch (Exception e) {
            logger.error("下载文件失败：{}", e);
        } finally {
            this.disconnect();
        }
        return success;
    }

    public String downloadAsStream(String directory, String downloadFile) {
        String content = "";
        try {
            ChannelSftp sftp = connect();
            if (sftp == null) {
                return null;
            }
            logger.info("Start to download the file {}, {} on remote server", directory, downloadFile);
            sftp.cd(directory);
            try (InputStream stream = sftp.get(downloadFile);
                 InputStreamReader isr = new InputStreamReader(stream);
                 BufferedReader br = new BufferedReader(isr);) {
                String tmp = "";
                while((tmp = br.readLine())!=null) {
                    content = content + tmp + System.getProperty("line.separator");
                }
                logger.info("成功下载文件, directory:{}, downloadFile:{}", directory, downloadFile);
            } catch (Exception e) {
                log.error(ExceptionUtils.getStackTrace(e));
                logger.error("下载文件失败", e);
            }
        } catch (Exception e) {
            log.error(ExceptionUtils.getStackTrace(e));
            logger.error("下载文件失败", e);
        } finally {
            this.disconnect();
        }
        return content;
    }

    /**
     * upload all the files to the server
     */
    public boolean upload(String localFile, String remoteDir, String remoteFileName) {
        try {
            ChannelSftp sftp = connect();
            if (sftp == null) {
                return false;
            }
            logger.info("The local file {}", localFile);
            File file = new File(localFile);
            if (file.isFile()) {
                logger.info("localFile: " + file.getAbsolutePath());
                logger.info("remoteFile: " + remoteDir + "/" + remoteFileName);
                createDir(sftp, remoteDir);
                //远程创建目录
                sftp.put(new FileInputStream(file), remoteDir + "/" + remoteFileName);
                logger.info("upload file successfully");
                return true;
            } else {
                logger.warn("The input file does not exist or is not a file");
            }
        } catch (FileNotFoundException e) {
            logger.error("上传文件到FTP失败：{}", e);
        } catch (SftpException e) {
            logger.error("上传文件到FTP失败：{}", e);
        } catch (Exception e) {
            logger.error("上传文件到FTP失败：{}", e);
        } finally {
            this.disconnect();
        }
        return false;
    }

    /**
     * upload by stream
     */
    public boolean uploadByStream(InputStream src, String remoteDir, String remoteFileName) {
        try {
            ChannelSftp sftp = connect();
            if (sftp == null) {
                return false;
            }
            if (src != null) {
                logger.info("remoteFile: " + remoteDir + "/" + remoteFileName);
                createDir(sftp, remoteDir);
                // 远程创建目录
                sftp.put(src, remoteDir + "/" + remoteFileName);
                logger.info("upload inputStream successfully");
                return true;
            } else {
                logger.warn("The inputstream is null");
            }
        } catch (FileNotFoundException e) {
            logger.error("上传InputStream到FTP失败：{}", e);
        } catch (SftpException e) {
            logger.error("上传InputStream到FTP失败：{}", e);
        } catch (Exception e) {
            logger.error("上传InputStream到FTP失败：{}", e);
        } finally {
            this.disconnect();
        }
        return false;
    }

    /**
     * 创建远程目录
     * @param channel
     * @param remoteDir 远程目录
     * @throws Exception
     */
    private static void createDir(ChannelSftp channel, String remoteDir) throws Exception {
    	logger.info("remoteDir is: " + remoteDir);
        int index = remoteDir.lastIndexOf("/");
        if (!isDirExist(channel, remoteDir)) {
        	if (index == -1) {
        		channel.mkdir(remoteDir);
        	} else {
	            String parentDir = remoteDir.substring(0, index);
	            createDir(channel, parentDir);
	        	logger.info("channel.mkdir(" + remoteDir + ")");
	            channel.mkdir(remoteDir);
        	}
        }
    }

    /**
     * 检测ftp服务器目录是否存在
     * @param channel
     * @param remoteDir ftp远程目录
     * @return
     * @throws Exception
     */
    private static boolean isDirExist(ChannelSftp channel, String remoteDir) throws Exception {
        boolean isDirExistFlag = false;
        try {
            isDirExistFlag = channel.lstat(remoteDir).isDir();
            return isDirExistFlag;
        } catch (Exception e) {
            isDirExistFlag = false;
        }
        return isDirExistFlag;
    }

    public ChannelSftp getSftp() {
        return sftp;
    }

}
